﻿using System;
//using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Mobile.Wurfl.BrowserUpdater
{
    using System;
    using System.Threading;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Contexts;
    using System.Runtime.Remoting.Messaging;
    using System.Web;
    using System.Net;
    using System.IO;
    using System.Xml.Linq;
    using System.IO.Compression;
    using Mobile.Device.Data;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Collections.Specialized;
    using Mobile.Utility.Lib.IO.Compression;

    public class WUrlfUpdater
    {
        static object lockSynch = new object();
        const int BufferSize = 8192;

        public void ExecuteUpdate(HttpApplication app)
        {
            lock (lockSynch)
            {


                UpdaterConfig config = null;

                if (app.Context.Cache["UpdaterConfig"] != null)
                {
                    config = app.Context.Cache["UpdaterConfig"] as UpdaterConfig;
                }
                else
                {
                    // Create a new file stream to write the serialized object to a file
                    XmlSerializer serializer = new XmlSerializer(typeof(UpdaterConfig));
                    using (TextReader fsr = new StreamReader(app.Server.MapPath("WurflConfig.xml")))
                        config = serializer.Deserialize(fsr) as UpdaterConfig;

                }

                //-- EXIT if we've already checked...
                TimeSpan timeSpan = DateTime.Now - config.LastUpdateChecked;

                //-- we already checked for an update..
                if (timeSpan.TotalMinutes < config.MaxCheckInterval)
                {
                    return;
                }

                //-- set last update check
                config.LastUpdateChecked = DateTime.Now;
                app.Context.Cache["UpdaterConfig"] = config;

                FeedItem feedItem = GetLastUpdateFeedInfo();
                List<Device> devices = null;
                string zippath = string.Empty;
                config.LastUpdated = DateTime.Now - new TimeSpan(200, 0, 0, 0);

                if (feedItem.PublishedDate > config.LastUpdated)
                {

                    string path = GetNewWurflXml(app, feedItem);
                    
                    
                    devices = GetDevicesFromWurfl(path);
                    string shimPath = WriteShim(app, devices, config);
                    string browserFilePath = app.Server.MapPath(@"App_Browsers\wurfl.browser");
                    SwitchFiles(shimPath, browserFilePath);
                    config.LastUpdated = feedItem.PublishedDate;
                }//if (feedItem.PublishedDate > lastUpdated)
            }//lockSynch

        }

        private void SwitchFiles(string shimPath,string browserFilePath)
        {
    
            File.Copy(shimPath, browserFilePath, true);

            File.Delete(shimPath);
        }

        private string WriteShim(HttpApplication app, List<Device> devices, UpdaterConfig config)
        {
            XmlShimConverterFactory factory = new XmlShimConverterFactory();
            XmlShimConverter shimConverter = factory.GetConverter(XmlConverterEnum.AppBrowser);
            string tempPath = app.Server.MapPath("temp_browser.xml");

            if (File.Exists(tempPath))
            {
                try
                {
                    File.Delete(tempPath);
                }
                catch (Exception)
                {
                    //do nada
                }
            }

            using (FileStream fs = new FileStream(tempPath, FileMode.Create))
            {
                using (XmlTextWriter xt = new XmlTextWriter(fs, Encoding.UTF8))
                {
                    xt.Formatting = Formatting.Indented;
                    shimConverter.WriteStart(xt, config.LastUpdated, devices);
                    foreach (var d in devices)
                    {
                        shimConverter.WriteDevice(d, xt, config.WurflAspMappings);
                    }//foreach
                    shimConverter.WriteEnd(xt);
                    xt.Flush();
                }//using
            }//using

            return tempPath;
        }

        private string GetNewWurflXml(HttpApplication app, FeedItem feedItem)
        {
            //Note - decompress in stream
            string zippath = app.Server.MapPath("wurfl_latest.zip");
            try
            {
                if (File.Exists(zippath))
                {
                    File.Delete(zippath);
                }
            }
            catch (Exception) { }

            WebRequest request = WebRequest.Create(feedItem.Url);
            using (WebResponse response = request.GetResponse())
            using (Stream responseStream = response.GetResponseStream())
            using (FileStream output = File.Create(zippath))
            {
                byte[] wbuffer = new byte[BufferSize];
                Copy(responseStream, output, wbuffer);
            }


            ZipLibrary Library;
            string zipdir = Path.GetDirectoryName(zippath) + @"\wurfuldata\";

            if (Directory.Exists(zipdir))
            {
                try
                {
                    Directory.Delete(zipdir);
                }
                catch (IOException)
                {
                    //nada
                }
            }

            using (Library = new ZipLibrary(zippath))
                Library.ExtractToDirectory(zipdir);


            string path = Directory.EnumerateFiles(zipdir).FirstOrDefault().ToString();

            //--delete the zip file
            try
            {
                if (File.Exists(zippath))
                {
                    File.Delete(zippath);
                }
            }
            catch (Exception) { }

            return path;
        }

        private FeedItem GetLastUpdateFeedInfo()
        {
            string url = "http://sourceforge.net/api/file/index/project-id/55408/mtime/desc/limit/20/rss";
            FeedItem item = new FeedItem();
            var xdoc = XDocument.Load(url);
            XNamespace rss = "http://purl.org/rss/1.0/modules/content/";

            var lv1s = from lv1 in xdoc.Descendants("item")
                       select new
                       {
                           Title = lv1.Element("title").Value,
                           PublishedDate = DateTime.Parse(lv1.Element("pubDate").Value),
                           Url = lv1.Element("link").Value
                       };

            string title = @"/WURFL/latest/wurfl-latest.zip";
            var zipItem = lv1s.FirstOrDefault(e => e.Title == title);

            if (zipItem != null)
            {
                item.PublishedDate = zipItem.PublishedDate;
                item.Title = zipItem.Title;
                item.Url = zipItem.Url;
            }
            return item;
        }


        private List<Device> GetDevicesFromWurfl(string path)
        {
            //--- ok now we got the xml
            List<Device> devices = new List<Device>();

            var xwurfl = XDocument.Load(path);
            var xdevices = from xdevices1 in xwurfl.Descendants("device")
                           select new
                           {
                               Id = xdevices1.Attribute("id").Value,
                               UserAgent = xdevices1.Attribute("user_agent").Value,
                               //ActualDeviceRoot = (xdevices1.Attributes("actual_device_root").FirstOrDefault() == null) ? false : (xdevices1.Attributes("actual_device_root").FirstOrDefault().Value == "true"),
                               FallBack = xdevices1.Attribute("fall_back").Value,
                               Groups = (from xgroup in xdevices1.Elements("group")
                                         select new
                                         {
                                             Id = xgroup.Attribute("id").Value,
                                             Capabilities = (from xcaps in xgroup.Elements("capability")
                                                             select new
                                                             {
                                                                 Name = xcaps.Attribute("name").Value,
                                                                 Value = xcaps.Attribute("value").Value
                                                             }
                                                            ).ToList()
                                         }
                                        ).ToList()
                           };



            foreach (var xd in xdevices)
            {
                var device = new Device();
                device.FallBack = xd.FallBack;
                device.Id = xd.Id;
                //device.ActualDeviceRoot = xd.ActualDeviceRoot;
                device.UserAgent = xd.UserAgent;
                device.Groups = new List<Group>();
                foreach (var xg in xd.Groups)
                {
                    Group g = new Group();
                    g.Id = xg.Id;
                    g.Capabilities = new List<Capability>();
                    foreach (var xc in xg.Capabilities)
                    {
                        Capability c = new Capability();
                        c.Name = xc.Name;
                        c.Value = xc.Value;
                        g.Capabilities.Add(c);
                    }//xc
                    device.Groups.Add(g);
                }//foreach xg

                devices.Add(device);
            }


            //-- now assign Fallback

            foreach (var d in devices)
            {
                foreach (var childDevice in devices)
                {
                    if (childDevice.Id == d.FallBack)
                    {
                        d.FallBackDevice = childDevice;
                        break;
                    }//if
                }//foreach
            }//(var d in devices)

            return devices;
        }//GetDevicesFromWurfl


        public void Copy(Stream input, Stream output, byte[] buffer)
        {
            int read;
            while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
            {
                output.Write(buffer, 0, read);
            }
        }



    }


}
